#include "mmp_lib.h"
#include "config_fw.h"

#if (EXT_CODEC_SUPPORT == 1)
#if (AUDEXT_DAC == AIC3254)
#include "lib_retina.h"
#include "mmp_reg_gbl.h"
#include "mmpd_system.h"
#include "mmps_audio.h"
#include "AIC3254.h"
#include "mmpf_i2cm.h"
#include "mmp_reg_audio.h"
#include "mmpf_usbuac.h"
//using BGPIO16 17
static MMPF_I2CM_ATTRIBUTE m_AIC3254_I2cmAttribute = {0, AIC3254_I2C_DEV_ADDR, 8, 8, 0, MMP_FALSE, MMP_FALSE, MMP_FALSE, MMP_FALSE, 0, 0, 3, MMPF_I2CM_SPEED_HW_250K, NULL, NULL/*MMPF_PIO_REG_GPIO16, MMPF_PIO_REG_GPIO17*/};
static MMP_BOOL m_bCodecInitialized = MMP_FALSE;
static MMP_BOOL m_bCodecAsMaster = MMP_TRUE;
static AUDIO_EXTCODEC_PATH m_ulCodecPath = AUDIO_EXT_PATH_MAX;
MMP_ERR MMPC_AudioExtCodec_SetRecordVolume(MMP_LONG dB);//unit is 0.1dB, 0.5dB for each step, input range is 0~-415

static MMP_USHORT current_page= 0xFF;
static const struct AIC3254_PLL_Setting AIC3254_SampleRate_Setting[] = {
/* 
 * rate, p_val, pll_j, pll_d, dosr, ndac, mdac, aosr, nadc, madc, blck_N, 
 * codec_speficic_initializations 
 */
	/* 8k rate */
	{8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24},
	/* 11.025k rate */
	{11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16},
	/* 16k rate */
	{16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12},
	/* 22.05k rate */
	{22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8},
	/* 32k rate */
	{32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6},
	/* 44.1k rate */
	{44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4},
	/* 48k rate */
	{48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4},
};

#define I2S_SLAVE_MODE  1

//*----------------------------------------------------------------------------
// function :MMPF_AIC3254_WriteReg
// input    : (Register address, Register data), reg 7 bits, data 9 bits
// output   : none
// descript : write codec register
//*----------------------------------------------------------------------------
void AIC3254_ReadReg(MMP_USHORT page_number, MMP_USHORT usReg, MMP_USHORT *usData)
{
    MMP_USHORT read = 0;
    MMP_ERR ret = MMP_ERR_NONE;
    
    if(current_page != page_number)
    {
        ret = MMPF_I2cm_WriteReg(&m_AIC3254_I2cmAttribute, 0, page_number);
        #if 0
        RTNA_DBG_Str0("Chage to Page:");
        RTNA_DBG_Long0(page_number);
        RTNA_DBG_Str0("\r\n");
        #endif
        if(ret == MMP_ERR_NONE)
            current_page = page_number;
    }
    
    MMPF_I2cm_ReadReg(&m_AIC3254_I2cmAttribute, usReg, usData);
    return;
}

//*----------------------------------------------------------------------------
// function :MMPF_AIC3254_WriteReg
// input    : (Register address, Register data), reg 7 bits, data 9 bits
// output   : none
// descript : write codec register
//*----------------------------------------------------------------------------
void AIC3254_WriteReg(MMP_USHORT page_number, MMP_USHORT usReg, MMP_USHORT usData)
{
    MMP_ERR ret = MMP_ERR_NONE;
    
    if(current_page != page_number)
    {
        ret = MMPF_I2cm_WriteReg(&m_AIC3254_I2cmAttribute, 0, page_number);
        #if 0
        RTNA_DBG_Str0("Chage to Page:");
        RTNA_DBG_Long0(page_number);
        RTNA_DBG_Str0("\r\n");
        #endif
        if(ret == MMP_ERR_NONE)
            current_page = page_number;
    }
    
    MMPF_I2cm_WriteReg(&m_AIC3254_I2cmAttribute, usReg, usData);
    return;
}
//*----------------------------------------------------------------------------
// function : InitExtDac
// input    : none
// output   : none
// descript : Initial external dac
//*----------------------------------------------------------------------------
void InitExtDac(void)
{
    int i = 0;
    MMP_ERR err = MMP_ERR_NONE;
    AITPS_GBL   pGBL = AITC_BASE_GBL;
    volatile MMP_UBYTE *REG_BASE_B = (volatile MMP_UBYTE *) (0x80000000);
    
    //REG_BASE_B[0x8D0A] = 0x00;
    pGBL->GBL_CLK_DIS0 &= ~(GBL_CLK_VI_DIS);
    pGBL->GBL_CLK_DIS1 &= ~(GBL_CLK_I2C_DIS);
   
    RTNA_DBG_Str0("InitExtDac \r\n");
    
    MMPF_I2cm_Initialize(&m_AIC3254_I2cmAttribute);

    AIC3254_WriteReg(0, AIC3254_HARDWARE_RESET,0x01); 
    
    AIC3254_WriteReg(1, AIC3254_POWER_CONFIG,0x08);
   
    AIC3254_WriteReg(1, AIC3254_LDO_CONFIG,0x00);
    
    if (m_bCodecAsMaster == MMP_FALSE) { // slave
        AIC3254_WriteReg(0, AIC3254_INTERFACE_SETTING1, RIGHT_JUSTIFIED_FORMAT | AUDIO_DATA_LENGTH_16);
    }
    else {
        AIC3254_WriteReg(0, AIC3254_INTERFACE_SETTING1, LEFT_JUSTIFIED_FORMAT | MASTER_MODE_ENABLE | AUDIO_DATA_LENGTH_16);
    }
}
//*----------------------------------------------------------------------------
// function : PowerUpExtDAC
// input    : none
// output   : none
// descript : Poweron external dac
//*----------------------------------------------------------------------------
void PowerUpExtDAC(MMP_USHORT sampling_rate)
{
    RTNA_DBG_Str0("PowerUpExtDAC \r\n");

    /*Headphone*/
    if (m_bCodecAsMaster == MMP_FALSE) { // slave
        //AIC3254_WriteReg(&m_AIC3254_I2cmAttribute, AIC3254_PWR_MGMT1, 0x1C0);//enable VREF
    }
    else {
        //AIC3254_WriteReg(&m_AIC3254_I2cmAttribute,  AIC3254_PWR_MGMT1, 0x1FC);//enable VREF, power up ADC for master mode
    }
}
//*----------------------------------------------------------------------------
// function : PowerDownExtDAC
// input    : none
// output   : none
// descript : Power down external dac
//*----------------------------------------------------------------------------
void PowerDownExtDAC(void)
{
    RTNA_DBG_Str0("PowerDownExtDAC \r\n");

    /*Headphone*/
}

//*----------------------------------------------------------------------------
// function : InitExtMicIn
// input    : none
// output   : none
// descript : Initial external mic in
//*----------------------------------------------------------------------------
void InitExtMicIn(void)
{
    MMPF_I2cm_Initialize(&m_AIC3254_I2cmAttribute);

    RTNA_DBG_Str0("InitExtMicIn \r\n");

    /*Mic*/
    //HW reset
    AIC3254_WriteReg(0, AIC3254_HARDWARE_RESET,0x01); 
    
    //MCLK (MHz) PLLP PLLR PLLJ PLLD MADC NADC AOSR MDAC NDAC DOSR
    //12          1     1   7   1680  2    7    128  7    2   128
    //PLL_clkin = MCLK, codec_clkin = PLL_CLK, PLL on, P=1, R=1, J=7, D=5264 PLL = 12 * 7.5164 = 
    AIC3254_WriteReg(0, AIC3254_PLL_SETTING1,0x03);
    AIC3254_WriteReg(0, AIC3254_PLL_SETTING2,0x91);//
    AIC3254_WriteReg(0, AIC3254_PLL_SETTING3,0x08);
    AIC3254_WriteReg(0, AIC3254_PLL_SETTING4,0x07);
    AIC3254_WriteReg(0, AIC3254_PLL_SETTING5,0x80);
    
    //NDAC = 2, MDAC = 8, dividers powered on
    AIC3254_WriteReg(0, AIC3254_PLL_SETTING6,0x88);
    AIC3254_WriteReg(0, AIC3254_PLL_SETTING7,0x82);
    
    //DOSR = 128
    AIC3254_WriteReg(0, 0x0D,0x00);
    AIC3254_WriteReg(0, 0x0E,0x80);
    
    //NADC = 2, MADC = 8, dividers powered on
    AIC3254_WriteReg(0, 0x12,0x88);
    AIC3254_WriteReg(0, 0x13,0x82);
    //AOSR = 128
    AIC3254_WriteReg(0, 0x14,0x80);
    
    //BCLK frequency is generated from ADC_CLK
    AIC3254_WriteReg(0, 0x1D,0x01);
    AIC3254_WriteReg(0, 0x1E,0x84);
    
    //Set BCLK and WCLK as outputs
    AIC3254_WriteReg(0, AIC3254_INTERFACE_SETTING1, 0xCC);
    AIC3254_WriteReg(0, 0x21,0x10);
#if 0    
    AIC3254_WriteReg(0, 0x14,0x80);
    AIC3254_WriteReg(0, 0x3D,0x01);
    AIC3254_WriteReg(1, 0x01,0x08);
    AIC3254_WriteReg(1, 0x02,0x00);
    AIC3254_WriteReg(1, 0x0A,0x00);
    AIC3254_WriteReg(1, 0x3D,0x00);
    AIC3254_WriteReg(1, 0x47,0x32);
    AIC3254_WriteReg(1, 0x34,0x80);
    AIC3254_WriteReg(1, 0x36,0x80);
    AIC3254_WriteReg(1, 0x37,0x80);
    AIC3254_WriteReg(1, 0x39,0x80);
    AIC3254_WriteReg(1, 0x3B,0x0C);
    AIC3254_WriteReg(1, 0x3C,0x0C);
    AIC3254_WriteReg(0, 0x51,0xC0);
    AIC3254_WriteReg(0, 0x52,0x00);
#endif    
}
//*----------------------------------------------------------------------------
// function : PowerUpExtMicIn
// input    : none
// output   : none
// descript : Poweron external mic in
//*----------------------------------------------------------------------------
void PowerUpExtMicIn(void)
{
    RTNA_DBG_Str0("PowerUpExtMicIn \r\n");
    
    //return;
    
    AIC3254_WriteReg(1, 0x01,0x00);
    AIC3254_WriteReg(1, 0x02,0x01);
    AIC3254_WriteReg(1, 0x3D,0x00);
    AIC3254_WriteReg(1, 0x47,0x32);
    AIC3254_WriteReg(1, 0x7B,0x01);
    
    // Power-up MIC BIA
    AIC3254_WriteReg(1, 0x33,0x40);
    //Route IN3L to LEFT_P with 10K input impedance
    AIC3254_WriteReg(1, 0x34,0x10);
    //Route IN3R to LEFT_M with 10K input impedance
    AIC3254_WriteReg(1, 0x36,0x10);
    AIC3254_WriteReg(1, 0x37,0x40);
    AIC3254_WriteReg(1, 0x39,0x10);
    //Unmute MICPGA
    AIC3254_WriteReg(1, 0x3B,0x00);
    AIC3254_WriteReg(1, 0x3C,0x00);
    
    //Power up ADC
    AIC3254_WriteReg(0, 0x51,0xC0);
    AIC3254_WriteReg(0, 0x52,0x00);
    
    AIC3254_WriteReg(0, 0x51,0xC0);
    AIC3254_WriteReg(0, 0x52,0x00);
    
    MMPC_AudioExtCodec_SetRecordVolume(VOL_DEF>>8);
}
//*----------------------------------------------------------------------------
// function : PowerDownExtMicIn
// input    : none
// output   : none
// descript : Power down external mic in
//*----------------------------------------------------------------------------
void PowerDownExtMicIn(void)
{
    MMP_USHORT val = 0;
    
    AIC3254_ReadReg(0, AIC3254_PLL_SETTING2, &val);
	AIC3254_WriteReg(0, AIC3254_PLL_SETTING2, (val & ~0x80));

	/* Switch off NDAC Divider */
	AIC3254_ReadReg(0, AIC3254_PLL_SETTING6, &val);
	AIC3254_WriteReg(0, AIC3254_PLL_SETTING6, val & ~0x80);

	/* Switch off MDAC Divider */
	AIC3254_ReadReg(0, AIC3254_PLL_SETTING7, &val);
	AIC3254_WriteReg(0, AIC3254_PLL_SETTING7, val & ~0x80);

	/* Switch off NADC Divider */
	AIC3254_ReadReg(0, AIC3254_PLL_SETTING8, &val);
	AIC3254_WriteReg(0, AIC3254_PLL_SETTING8, val & ~0x80);

	/* Switch off MADC Divider */
	AIC3254_ReadReg(0, AIC3254_PLL_SETTING9, &val);
	AIC3254_WriteReg(0, AIC3254_PLL_SETTING9, val & ~0x80);

	/* Switch off BCLK_N Divider */
	AIC3254_ReadReg(0, AIC3254_PLL_SETTING11, &val);
	AIC3254_WriteReg(0, AIC3254_PLL_SETTING11, val & ~0x80);
    RTNA_DBG_Str0("PowerDownExtMicIn \r\n");

    /*Mic*/
    //AIC3254_WriteReg(&m_AIC3254_I2cmAttribute, AIC3254_PWR_MGMT1,0x000);
}

void AGCControl(MMP_UBYTE bEnable, MMP_USHORT usTargetLevel)
{
    MMP_USHORT val = 0;
    
    

    /*Mic*/
    //AIC3254_WriteReg(&m_AIC3254_I2cmAttribute, AIC3254_PWR_MGMT1,0x000);
}

/** @brief Inhouse Wolfson audio CODEC initializer.

See @ref AudioInitializer for more information.
*/
MMP_BOOL AudioInitializer(MMP_USHORT cmd)
{
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_TRUE);
    if (cmd & AUDIO_INIT_OUT) {
    	//InitExtDac();
    	//PowerUpDACOu(32000);
    }

    if (cmd & AUDIO_INIT_MICIN) {
        InitExtMicIn();
        PowerUpExtMicIn();
    }
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_FALSE);

    return MMP_TRUE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetSampleRate
// input    : ulSamplerate: external codec runs in USB mode needs to change PLL 
//            according to sample rate
// output   : none
// descript : Config external codec PLL to specified sample rate,
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetSampleRate(MMP_ULONG ulSamplerate)
{
    MMP_USHORT i = 0;
    
    //return 0;
    RTNA_DBG_Str(0, "MMPC_AudioExtCodec_SetSampleRate\r\n");
    RTNA_DBG_PrintLong(0,ulSamplerate);
    
    for(i = 0; i< 7; i++)
        if(AIC3254_SampleRate_Setting[i].samplerate == ulSamplerate)
            break;
    if(i == 7)
    {
        RTNA_DBG_Str(0, "Not support this sample rate\r\n");
        return MMP_ERR_NONE;
    }
    RTNA_DBG_PrintLong(0,AIC3254_SampleRate_Setting[i].samplerate);
    AIC3254_WriteReg(0, AIC3254_PLL_SETTING2,((AIC3254_SampleRate_Setting[i].p_val << 4) | 0x01) | 0x80);

	/* J value */
	AIC3254_WriteReg(0, AIC3254_PLL_SETTING3, AIC3254_SampleRate_Setting[i].pll_j);

	/* MSB & LSB for D value */
	AIC3254_WriteReg(0, AIC3254_PLL_SETTING4, (AIC3254_SampleRate_Setting[i].pll_d >> 8));
	AIC3254_WriteReg(0, AIC3254_PLL_SETTING5, (AIC3254_SampleRate_Setting[i].pll_d & 0xFF));

	/* NDAC divider value */
	AIC3254_WriteReg(0, AIC3254_PLL_SETTING6, AIC3254_SampleRate_Setting[i].ndac | 0x80);

	/* MDAC divider value */
	AIC3254_WriteReg(0, AIC3254_PLL_SETTING7, AIC3254_SampleRate_Setting[i].mdac | 0x80);

	/* DOSR MSB & LSB values */
	AIC3254_WriteReg(0, 0x0D, AIC3254_SampleRate_Setting[i].dosr >> 8);
	AIC3254_WriteReg(0, 0x0E, AIC3254_SampleRate_Setting[i].dosr & 0xFF);

	/* NADC divider value */
	AIC3254_WriteReg(0, 0x12, AIC3254_SampleRate_Setting[i].nadc | 0x80);

	/* MADC divider value */
	AIC3254_WriteReg(0, 0x13, AIC3254_SampleRate_Setting[i].madc | 0x80);

	/* AOSR value */
	AIC3254_WriteReg(0, 0x14, AIC3254_SampleRate_Setting[i].aosr);
	/* BCLK N divider */
	AIC3254_WriteReg(0, 0x1E, AIC3254_SampleRate_Setting[i].blck_N | 0x80);
	
    return MMP_ERR_NONE;
}
//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetMute
// input    : bMute: set MMP_TRUE to mute, MMP_FALSE to un-mute 
// output   : none
// descript : Config external codec mute/un-mute.
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetMute(MMP_BOOL bMute)//work
{
    MMP_USHORT usMute = 0;
    
    MMPF_I2cm_WriteReg(&m_AIC3254_I2cmAttribute, 0,0);
    MMPF_I2cm_ReadReg(&m_AIC3254_I2cmAttribute, 0x52, &usMute);
    
    if (bMute) {
        RTNA_DBG_Str(0, "MMPC_AudioExtCodec_SetMute\r\n");
        AIC3254_WriteReg(0, 0x52,0x88 | usMute);
    }
    else {
        RTNA_DBG_Str(0, "MMPC_AudioExtCodec_SetUnmute\r\n");
        AIC3254_WriteReg(0, 0x52,usMute & (~0x88));
    }

    return MMP_ERR_NONE;
}
//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetVolume
// input    : level: external codec volume level
// output   : none
// descript : Config external codec volume level,
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetVolume(MMP_ULONG level)
{
    return MMP_ERR_NONE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetVolume
// input    : level: external codec volume level
// output   : none
// descript : Config external codec volume level,
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetRecordVolume(MMP_LONG value)//unit is 0.1dB, 0.1dB for each step, input range is 0~-415
{
    //MMP_LONG value = 0;
    MMP_USHORT finedB = 0;
    
    
    //value = ((dB + 4) / 5);
    
   // value = dB;
    if(value < 0)
        value = 0;
    else if(value > 95)
        value = 95;    
        
    AIC3254_WriteReg(1, 0x3B,value);
    AIC3254_WriteReg(1, 0x3C,value);
    dbg_printf(0, "value:%x\r\n", value);
    #if 0
    value = dB & 0x07;
    if(value != 0)
    {
	    MMPF_I2cm_ReadReg(&m_AIC3254_I2cmAttribute, 0x52, &finedB);
	    finedB &= 0x88;
	    value = 5 - value;
	    finedB |= (value << 4| value);
	    if(finedB <0xFF)
	    {
	        AIC3254_WriteReg(0, 0x52,finedB);
	        RTNA_DBG_PrintLong(0,finedB);
	    }
    }
    #endif
    
    return MMP_ERR_NONE;
}
//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetPath
// input    : path: please refer MMPF_AUDIO_EXTCODEC_PATH for path can be set
// output   : none
// descript : Config external codec path if alreay iniialized, otherwise,
//            keep the path settings only.
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetPath(MMP_ULONG path)
{
    if (m_bCodecInitialized == MMP_TRUE) {
        //reset codec path
        switch (m_ulCodecPath) {
        case AUDIO_EXT_HP_OUT:
            AudioInitializer(AUDIO_INIT_OUT);
            break;
        case AUDIO_EXT_MIC_IN:
            AudioInitializer(AUDIO_INIT_MICIN);
            break;
        case AUDIO_EXT_AUX_IN:
            AudioInitializer(AUDIO_INIT_LINEIN);
            break;
        case AUDIO_EXT_AUX_BYPASS_HP:
            AudioInitializer(AUDIO_INIT_OUT_LINEIN);
            break;
        default:
            return MMP_AUDIO_ERR_PARAMETER;
            break;
        }
    }
    m_ulCodecPath = path;

    return MMP_ERR_NONE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_Init
// input    : samplerate: Sample rate
//            bSetAsMaster: Set codec as master mode or slave mode
// output   : none
// descript : Initialize and power up codec according to path settings,
//            be careful, please call MMPC_AudioExtCodec_SetPath first to set path
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_Init(MMP_ULONG samplerate, MMP_ULONG ulFixedMclkFreq)
{
    if (m_ulCodecPath == AUDIO_EXT_PATH_MAX) {
        RTNA_DBG_Str0("Please set path first before init codec!\r\n");
        return MMP_AUDIO_ERR_INVALID_FLOW;
    }
    //if (I2SConfig->workingMode == MMPS_AUDIO_I2S_MASTER_MODE) {
        //set AIT as master, set codec as slave
        //m_bCodecAsMaster = MMP_FALSE;
    //}
   // else {
        //set AIT as slave, set codec as master
    //    m_bCodecAsMaster = MMP_TRUE;
    //}

    switch (m_ulCodecPath) {
    case AUDIO_EXT_HP_OUT:
        AudioInitializer(AUDIO_INIT_OUT);
        break;
    case AUDIO_EXT_MIC_IN:
        AudioInitializer(AUDIO_INIT_MICIN);
        break;
    case AUDIO_EXT_AUX_IN:
        AudioInitializer(AUDIO_INIT_LINEIN);
        break;
    case AUDIO_EXT_AUX_BYPASS_HP:
        AudioInitializer(AUDIO_INIT_OUT_LINEIN);
        break;
    default:
        return MMP_AUDIO_ERR_PARAMETER;
        break;
    }
    m_bCodecInitialized = MMP_TRUE;

    return MMP_ERR_NONE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_Uninit
// input    : none
// output   : none
// descript : Uninitialize and power down codec according to path settings.
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_Uninit(void)
{
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_TRUE);

    if (m_bCodecInitialized) {
        switch (m_ulCodecPath) {
        case AUDIO_EXT_HP_OUT:
            //PowerDownExtDAC();
            break;
        case AUDIO_EXT_MIC_IN:
            PowerDownExtMicIn();
            break;
        case AUDIO_EXT_AUX_IN:
            //PowerDownExtLineIn();
            break;
        case AUDIO_EXT_AUX_BYPASS_HP:
            //PowerDownExtDAC();
            //PowerDownExtLineIn();
            break;
        default:
            return MMP_AUDIO_ERR_PARAMETER;
            break;
        }
        m_bCodecInitialized = MMP_FALSE;
        //m_ulCodecPath = MMPF_AUDIO_EXT_PATH_MAX;
    }
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_FALSE);

    return MMP_ERR_NONE;
}
#endif
#endif